
#include <iostream>
#include <vector>
#include <algorithm>

#include <boost/foreach.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>

#include <logger.hpp>
#include <xbase_utility.hpp>

using namespace std;

#include <fs.hpp>

namespace fs = xbase::fs;
using fs::path;

struct file_info_less 
{
	bool operator() (const fs::file_info &lhs,
			 const fs::file_info &rhs) const {
		return fs::get_name(lhs) < fs::get_name(rhs);
	}
};

int do_cmd(string cmd, string arg)
{
	if (cmd == "ls")
	{
		std::vector<fs::file_info> _files;
		bool _ret = fs::list_files(_files, arg);
		if (_ret)
		{
			// sort
			std::sort(_files.begin(), _files.end(), file_info_less());
			
			cout << arg << ":" << endl;
			BOOST_FOREACH(fs::file_info _info, _files)
			{
				cout << (fs::is_regular(_info)
					 ? "f"
					 : (fs::is_directory(_info)
					    ? "d" :
					    "o")) << "\t"
				     << get_name(_info)
				     << endl;
			}
		}
		else
			cout << "ls failed." << endl;
	}
	else if (cmd == "lsr")
	{
		std::vector<std::list<fs::file_info> > dir_level;
		std::vector<std::string> dir_name;
		const std::string space_list(1024, ' ');

		std::list<fs::file_info> _files;
		bool _ret = fs::list_files(_files, arg);
		if (_ret)
		{
			// sort
			//std::sort(_files.begin(), _files.end(), file_info_less());
			_files.sort(file_info_less());
			dir_level.push_back(_files);
			dir_name.push_back(arg);

			cout << arg << endl;
			while(! dir_level.empty())
			{
				std::list<fs::file_info> *cur_dir = &dir_level.back();
				while(! cur_dir->empty())
				{
					fs::file_info info = cur_dir->front();
					cout << space_list.substr(0, dir_level.size() * 2)
					     << (fs::is_regular(info)
						 ? "f"
						 : (fs::is_directory(info)
						    ? "d"
						    : "o")) << "  "
					     << fs::get_name(info)
					     << endl;
					cur_dir->pop_front();

					if(fs::is_directory(info))
					{
						std::list<fs::file_info> info_list;
						std::string cur_dir_name = dir_name.back() + "/" + fs::get_name(info);
						bool ret = fs::list_files(info_list, cur_dir_name);
						if(! ret)
						{
							cout << "list dir "
							     << cur_dir_name
							     << " failed" << endl;
							return -1;
						}

						//std::sort(info_list.begin(), info_list.end(), file_info_less());
						info_list.sort(file_info_less());
						dir_level.push_back(info_list);
						dir_name.push_back(cur_dir_name);
						break;
					}
				}
				if(cur_dir->empty() && cur_dir == &dir_level.back())
				{
					dir_level.pop_back();
					dir_name.pop_back();
				}
			}
		}
		else
			cout << "ls failed." << endl;
	}
	else if (cmd == "stat")
	{
		fs::file_status _status;
		bool _ret = fs::stat(_status, arg);
		if (_ret) {
			cout << (fs::is_regular(_status) ? "f" : "d") << "\t"
			     << arg
			     << endl;
		} else {
			cout << "stat " << arg << " failed(" << fs::get_errno() << ")." << endl;
		}
	}
	else if (cmd == "mkdir")
	{
		bool _ret = fs::mkdir(arg);
		if (_ret) {
			cout << arg << " created." << endl;
		} else {
			cout << "mkdir " << arg << " failed(" << fs::get_errno() << ")." << endl;
		}
	}
	else if (cmd == "touch")
	{
		fs::file_t _ret = fs::create(arg);
		if (_ret != fs::BAD_FILE) {
			cout << arg << " created." << endl;
			fs::close(_ret);
		} else {
			cout << "touch " << arg << " failed(" << fs::get_errno() << ")." << endl;
		}
	}
	else if (cmd == "rm")
	{
		bool _ret = fs::remove(arg);
		if (_ret) {
			cout << arg << " removed." << endl;
		} else {
			cout << "rm " << arg << " failed(" << fs::get_errno() << ")." << endl;
		}
	}
	else if (cmd == "write")
	{
		bool exists = fs::exists(arg);
		fs::file_t file;
		if(exists)
		{
			file = fs::open(arg, fs::MT_O_WRONLY);
		}
		else
		{
			file = fs::create(arg);
		}
		if(file == fs::BAD_FILE)
		{
			cout << (exists ? "open" : "create") << " file failed" << endl;
			return -1;
		}
		cout << (exists ? "open" : "create") << " file ok" << endl;
		fs::offset_t ret = fs::seek(file, 0, fs::ST_SEEK_END);
		if(ret < 0)
		{
			cout << "seek end failed" << endl;
			return -1;
		}
		const std::string line_end("\n");
		std::string data;
		while(cin >> data)
		{
			data += line_end;
			fs::ssize_t ret = fs::write(file, data.data(), data.size());
			if(ret < 0)
			{
				cout << "write error" << endl;
				return -1;
			}
			if(ret < (fs::ssize_t)data.size())
			{
				cout << "just write " << ret << " of " << data.size() << endl;
			}
		}
		bool ok = fs::close(file);
		if(! ok)
		{
			cout << "close file failed" << endl;
			return -1;
		}
	}
	else if (cmd == "writen")
	{
		bool exists = fs::exists(arg);
		fs::file_t file;
		if(exists)
		{
			file = fs::open(arg, fs::MT_O_WRONLY);
		}
		else
		{
			file = fs::create(arg);
		}
		if(file == fs::BAD_FILE)
		{
			cout << (exists ? "open" : "create") << " file failed" << endl;
			return -1;
		}
		cout << (exists ? "open" : "create") << " file ok" << endl;
		fs::offset_t ret = fs::seek(file, 0, fs::ST_SEEK_END);
		if(ret < 0)
		{
			cout << "seek end failed" << endl;
			return -1;
		}
		const std::string line_end("\n");
		std::string data;
		while(cin >> data)
		{
			data += line_end;
			fs::ssize_t ret = fs::writen(file, data.data(), data.size());
			if(ret < 0)
			{
				cout << "writen error" << endl;
				return -1;
			}
			if(ret < (fs::ssize_t)data.size())
			{
				cout << "just write " << ret << " of " << data.size() << endl;
			}
		}
		bool ok = fs::close(file);
		if(! ok)
		{
			cout << "close file failed" << endl;
			return -1;
		}
	}
	else if (cmd == "writev")
	{
		bool exists = fs::exists(arg);
		fs::file_t file;
		if(exists)
		{
			file = fs::open(arg, fs::MT_O_WRONLY);
		}
		else
		{
			file = fs::create(arg);
		}
		if(file == fs::BAD_FILE)
		{
			cout << (exists ? "open" : "create") << " file failed" << endl;
			return -1;
		}
		cout << (exists ? "open" : "create") << " file ok" << endl;
		fs::offset_t ret = fs::seek(file, 0, fs::ST_SEEK_END);
		if(ret < 0)
		{
			cout << "seek end failed" << endl;
			return -1;
		}

		std::vector<boost::asio::const_buffer> data_vec;
		fs::ssize_t total_size = 0;

		const std::string line_end("\n");
		std::string data;
		while(cin >> data)
		{
			data += line_end;
			data_vec.push_back(boost::asio::buffer(data));
			total_size += data.size();
		}

		// 一次性写入数据
		
		fs::ssize_t ret_n = fs::writen(file, data_vec);
		if(ret_n < 0)
		{
			cout << "writev error" << endl;
			return -1;
		}
		if(ret_n < total_size)
		{
			cout << "just write " << ret_n << " of " << data.size() << endl;
		}
		if(ret_n == total_size)
		{
			cout << "write " << data_vec.size() << " lines with "
			     << total_size << " bytes once" << endl;
		}

		bool ok = fs::close(file);
		if(! ok)
		{
			cout << "close file failed" << endl;
			return -1;
		}
	}
	else if (cmd == "append")
	{
		bool exists = fs::exists(arg);
		fs::file_t file;
		if(exists)
		{
			file = fs::open(arg, fs::MT_O_WRONLY);
		}
		else
		{
			file = fs::create(arg);
		}
		if(file == fs::BAD_FILE)
		{
			cout << (exists ? "open" : "create") << " file failed" << endl;
			return -1;
		}
		cout << (exists ? "open" : "create") << " file ok" << endl;
		fs::offset_t ret = fs::seek(file, 0, fs::ST_SEEK_END);
		if(ret < 0)
		{
			cout << "seek end failed" << endl;
			return -1;
		}
		const std::string line_end("\n");
		std::string data;
		while(cin >> data)
		{
			data += line_end;
			fs::ssize_t ret = fs::append(file, data.data(), data.size());
			if(ret < 0)
			{
				cout << "append error" << endl;
				return -1;
			}
			if(ret < (fs::ssize_t)data.size())
			{
				cout << "just write " << ret << " of " << data.size() << endl;
			}
		}
		bool ok = fs::close(file);
		if(! ok)
		{
			cout << "close file failed" << endl;
			return -1;
		}
	}
	else if (cmd == "cat")
	{
		fs::file_t file = fs::open(arg);
		if(file == fs::BAD_FILE)
		{
			cout << "open" << " file failed" << endl;
			return -1;
		}
		const int buffer_len = 1024 * 512; // 512K
		char buffer[buffer_len];
		bool eof = false;
		while(! eof)
		{
			fs::ssize_t ret = fs::readn(file, buffer, buffer_len); // use readn
			if(ret < 0)
			{
				cout << "readn error" << endl;
				return -1;
			}
			if(ret < buffer_len)
			{
				cout << "just read " << ret << " of " << buffer_len << endl;
				eof = true;
			}
			std::string data(buffer, ret);
			cout << dump_string_char(data);
		}
		bool ok = fs::close(file);
		if(! ok)
		{
			cout << "close file failed" << endl;
			return -1;
		}
	}
	else
	{
		cout << "nothing to do." << endl;
	}

	return 0;
}


int main(int argc, char **argv)
{
	string cmd;//(argv[1]);
	string arg;//(argv[2]);

	LOG_CONFIG("../../conf/log4cplus.tablet_server.conf");
	fs::init("../../conf/gfsclient.conf");

	while (cin >> cmd >> arg)
	{
		int n = 10;

		while(--n)
		{
			//boost::thread thread(boost::bind(&do_cmd, cmd, arg));
			do_cmd(cmd, arg);
			sleep(1);
		}
	}
}
